# asterisk-cos.pm
# Copyright (C) 2006,2007  Stephane Alnet
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 3
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
# 

#
# For more information: http://carrierclass.net/
#
package CCNv2::Provision::Asterisk::cos;
use strict;
use warnings;
use base qw(CCNCore::Provision::Base);
use CCN::Text;
use CCNCore::Scripting;

# asterisk_cos()
#   Implement the CoS ("Class of Service") for Asterisk.

sub run()
{
    my $self = shift;
    return unless defined $self->p->{cp_cluster};

    # ------------ CoS --------------
    my $astgroupconf = '';
    my $astringsconf = '';

    $self->sql_do_for( <<'SQL', $self->p->{cp_cluster},
        SELECT map.enterprise, map.name, pattern, pattern_is_prefix, keep, skip, prepend, context, ring_group, ring_group_use
        FROM map_patterns, map
        WHERE map_patterns.map = map.name AND map.enterprise IN (SELECT DISTINCT enterprise FROM pool WHERE cp_cluster = ?);
SQL
    sub
    {
        my ($enterprise_pk,$map_name,$pattern,$pattern_is_prefix,$keep,$skip,$prepend,$context_name,$ring_group_pk,$ring_group_use) = @_;

        my $pool;
        my $cos = $map_name;
        $pattern_is_prefix = defined $pattern_is_prefix and $pattern_is_prefix =~ /^t/i;

        if( $map_name =~ /^local-map-(\S+)$/ )
        {
            $pool = $1;
        }
        else
        {
            $pool = $enterprise_pk;
        }
        $astgroupconf .= "[${map_name}]\n";

        #- The entries inside a "group local" statement are available in the enterprise
        #- call routing plan as the "group-local" CoS.

        my $num = $pattern;
        my $target = '${EXTEN}';

        # 'number' patterns are rewritten as Asterisk matches, while names are left as-is. 
        if( $pattern =~ /^[#*\dX]+$/ )
        {
            # No need to make it a pattern if it's simply a number.
            $num = "_${pattern}" if $pattern =~ /X/;

            # $keep is the number of digits we want to keep at the end of the pattern.
            $keep ||= 0;
            # $skip is the number of digits we want to remove at the beginning of the pattern.
            $skip ||= 0;
            # Remove is the actual number of digits that will get removed at the beginning.
            my $remove = length($pattern)-$keep + $skip;
            $target = "$prepend\${EXTEN:${remove}}";

            # If the pattern is a prefix, indicate so.
            $num .= '.' if $pattern_is_prefix;
        }

        # Fields are computed. Figure out what we actually have to do.
        if( defined $context_name )
        {
            # Target is actually a context.
            $astgroupconf .= "exten => ${num},1,Goto(${enterprise_pk}-${context_name},s,1)\n";
        }
        elsif( defined $ring_group_pk )
        {
            # Target is a "ring group".
            my @exts = $self->sql_get_array( <<'SQL', $ring_group_pk);
                SELECT target FROM ring_group_targets
                WHERE pk = ? ORDER BY rank ASC;
SQL

            if( $ring_group_use == 1 )
            {
                # Use = parallel
                @exts = map {"Local/$_\@\${POOL}-cos-${cos}"} @exts;
                my $dial = join('&',@exts);

                $astgroupconf .=
                    "exten => ${num},1,Goto(${pool}-${map_name}-${pattern},s,1)\n";

                $astringsconf .=
                    "[${pool}_-${map_name}-${pattern}]\n".
                    "exten => s,1,Dial(${dial},,r)\n";

            }
            elsif( $ring_group_use == 2 or $ring_group_use == 3 )
            {
                # Use = bottom-up
                @exts = reverse @exts if $ring_group_use == 3;
                # Use = top-down
                for my $n (0..$#exts) {
                    $exts[$n] = "exten => s,".($n+1).",Dial(Local/$exts[$n]\@\${POOL}-cos-${cos},15,r)\n"; 
                }
                my $dial = join('',@exts);

                $astgroupconf .=
                    "exten => ${num},1,Goto(${pool}-${map_name}-${pattern},s,1)\n";
                
                $astringsconf .=
                    "[${pool}-${map_name}-${pattern}]\n".$dial;
            }
        }
        else
        {
            # Target is a name or number that gets evaluated in the target CoS.
            $astgroupconf .= "exten => ${num},1,Goto(\${POOL}-cos-${cos},${target},1)\n";
        }
    });

    foreach("/etc/asterisk/config/extensions.groups.conf")
    {
        CAT $_, $astgroupconf . $astringsconf;
        IF_CHANGED $_ => CCNv2::Scripting::AST('extensions reload');
    }

    my $cosconf = copyright(';');

    # Write CoS at the enterprise level
    # YYY See if we can merge the two SQL queries?
    $self->sql_do_for( <<'SQL', $self->p->{cp_cluster},
        SELECT enterprise, name, invalid FROM entcoslist
        WHERE enterprise IN (SELECT DISTINCT enterprise FROM pool WHERE cp_cluster = ?);
SQL
    sub
    {
        my ($ENTERPRISE,$COS,$invalid) = @_;

        $cosconf .= "; enterprise $ENTERPRISE\n".
                    "; cos $COS\n".
                    "[${ENTERPRISE}-cos-${COS}]\n";

        $self->sql_do_for( <<'SQL',
            SELECT route_name, rank FROM cos_route
            WHERE enterprise_name = ? AND cos_name = ? ORDER BY rank ASC;
SQL
        $ENTERPRISE, $COS,
        sub
        {
            my $route_name = shift;
            $cosconf .= "include => ${ENTERPRISE}-$route_name\n";
        });

        $cosconf .=
            "exten => i,1,Goto(${ENTERPRISE}-${invalid},s,1)\n"
                if defined $invalid;

    });

    # YYY See if we can merge the two SQL queries?
    $self->sql_do_for( <<'SQL', $self->p->{cp_cluster},
        SELECT enterprise, pool, name, invalid FROM poolcoslist
        WHERE pool IN (SELECT pk FROM pool WHERE cp_cluster = ?);
SQL
    sub
    {
        my ($ENTERPRISE,$POOL,$COS,$invalid) = shift;

        $cosconf .= "; pool $POOL\n".
                    "; cos $COS\n".
                    "[${POOL}-cos-${COS}]\n";

        $self->sql_do_for( <<'SQL',
            SELECT route, rank FROM cos_route
            WHERE cos = ? ORDER BY rank ASC;
SQL
        $COS,
        sub
        {
            my $route_name = shift;
            if( $route_name =~ /-local$/ )
            {
                $cosconf .= "include => ${POOL}-$route_name\n";
            }
            else
            {
                $cosconf .= "include => ${ENTERPRISE}-$route_name\n";
            }
        });

        $cosconf .=
            "exten => i,1,Goto(${ENTERPRISE}-${invalid},s,1)\n"
                if defined $invalid;
    });

    foreach("/etc/asterisk/config/extensions.cos.conf")
    {
        CAT $_, $cosconf;
        IF_CHANGED $_ => CCNv2::Scripting::AST('extensions reload');
    }
}
1;
